package com.qrcode.scan;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;

import net.sourceforge.zbar.Config;
import net.sourceforge.zbar.Image;
import net.sourceforge.zbar.ImageScanner;
import net.sourceforge.zbar.Symbol;

import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import cn.bingoogolapple.qrcode.core.BGAQRCodeUtil;
import cn.bingoogolapple.qrcode.core.BarcodeType;
import cn.bingoogolapple.qrcode.core.QRCodeView;
import cn.bingoogolapple.qrcode.core.ScanResult;


/**
 * 项目名称：com.example.zxing.utils
 * 创建人:LuoFG
 * 创建时间:2021/10/21  15:01
 * 描述: Zbar Zxing一起识别 哪个有结果用哪个
 * 作用:
 */
public class ScanView extends QRCodeView {

    static {
        System.loadLibrary("iconv");
    }

    private ImageScanner mScanner;
    private List<ScanCodeFormat> mFormatList;
    private MultiFormatReader mMultiFormatReader;
    private Map<DecodeHintType, Object> mHintMap;

    public ScanView(Context context, AttributeSet attributeSet) {
        this(context, attributeSet, 0);
    }

    public ScanView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setupReader();
        setZxingUpReader();
    }

    @Override
    protected void setupReader() {
        mScanner = new ImageScanner();
        mScanner.setConfig(0, Config.X_DENSITY, 3);
        mScanner.setConfig(0, Config.Y_DENSITY, 3);

        mScanner.setConfig(Symbol.NONE, Config.ENABLE, 0);

        for (ScanCodeFormat format : getFormats()) {
            mScanner.setConfig(format.getId(), Config.ENABLE, 1);
        }
    }

    protected void setZxingUpReader() {
        mMultiFormatReader = new MultiFormatReader();

        if (mBarcodeType == BarcodeType.ONE_DIMENSION) {
            mMultiFormatReader.setHints(ZxingCodeDecoder.ONE_DIMENSION_HINT_MAP);
        } else if (mBarcodeType == BarcodeType.TWO_DIMENSION) {
            mMultiFormatReader.setHints(ZxingCodeDecoder.TWO_DIMENSION_HINT_MAP);
        } else if (mBarcodeType == BarcodeType.ONLY_QR_CODE) {
            mMultiFormatReader.setHints(ZxingCodeDecoder.QR_CODE_HINT_MAP);
        } else if (mBarcodeType == BarcodeType.ONLY_CODE_128) {
            mMultiFormatReader.setHints(ZxingCodeDecoder.CODE_128_HINT_MAP);
        } else if (mBarcodeType == BarcodeType.ONLY_EAN_13) {
            mMultiFormatReader.setHints(ZxingCodeDecoder.EAN_13_HINT_MAP);
        } else if (mBarcodeType == BarcodeType.HIGH_FREQUENCY) {
            mMultiFormatReader.setHints(ZxingCodeDecoder.HIGH_FREQUENCY_HINT_MAP);
        } else if (mBarcodeType == BarcodeType.CUSTOM) {
            mMultiFormatReader.setHints(mHintMap);
        } else {
            mMultiFormatReader.setHints(ZxingCodeDecoder.ALL_HINT_MAP);
        }
    }

    public Collection<ScanCodeFormat> getFormats() {
        if (mBarcodeType == BarcodeType.ONE_DIMENSION) {
            return ScanCodeFormat.ONE_DIMENSION_FORMAT_LIST;
        } else if (mBarcodeType == BarcodeType.TWO_DIMENSION) {
            return ScanCodeFormat.TWO_DIMENSION_FORMAT_LIST;
        } else if (mBarcodeType == BarcodeType.ONLY_QR_CODE) {
            return Collections.singletonList(ScanCodeFormat.QRCODE);
        } else if (mBarcodeType == BarcodeType.ONLY_CODE_128) {
            return Collections.singletonList(ScanCodeFormat.CODE128);
        } else if (mBarcodeType == BarcodeType.ONLY_EAN_13) {
            return Collections.singletonList(ScanCodeFormat.EAN13);
        } else if (mBarcodeType == BarcodeType.HIGH_FREQUENCY) {
            return ScanCodeFormat.HIGH_FREQUENCY_FORMAT_LIST;
        } else if (mBarcodeType == BarcodeType.CUSTOM) {
            return mFormatList;
        } else {
            return ScanCodeFormat.ALL_FORMAT_LIST;
        }
    }

    /**
     * 设置识别的格式 Zbar
     *
     * @param barcodeType 识别的格式
     * @param formatList  barcodeType 为 BarcodeType.CUSTOM 时，必须指定该值
     */
    public void setType(BarcodeType barcodeType, List<ScanCodeFormat> formatList) {
        mBarcodeType = barcodeType;
        mFormatList = formatList;

        if (mBarcodeType == BarcodeType.CUSTOM && (mFormatList == null || mFormatList.isEmpty())) {
            throw new RuntimeException("barcodeType 为 BarcodeType.CUSTOM 时 formatList 不能为空");
        }
        setupReader();
    }

    /**
     * 设置识别的格式 Zxing
     *
     * @param barcodeType 识别的格式
     * @param hintMap     barcodeType 为 BarcodeType.CUSTOM 时，必须指定该值
     */
    public void setType(BarcodeType barcodeType, Map<DecodeHintType, Object> hintMap) {
        mBarcodeType = barcodeType;
        mHintMap = hintMap;

        if (mBarcodeType == BarcodeType.CUSTOM && (mHintMap == null || mHintMap.isEmpty())) {
            throw new RuntimeException("barcodeType 为 BarcodeType.CUSTOM 时 hintMap 不能为空");
        }
        setZxingUpReader();
    }

    @Override
    protected ScanResult processData(byte[] data, int width, int height, boolean isRetry) {
        String result=getZbarResult(data,width,height,isRetry);
        if(TextUtils.isEmpty(result)){
            result=getZxingResult(data,width,height,isRetry);
        }
        return new ScanResult(result);
    }

    /**
     * Zbar识别
     */
    private String getZbarResult(byte[] data, int width, int height, boolean isRetry){
        Image barcode = new Image(width, height, "Y800");

        Rect scanBoxAreaRect = mScanBoxView.getScanBoxAreaRect(height);
        if (scanBoxAreaRect != null && !isRetry && scanBoxAreaRect.left + scanBoxAreaRect.width() <= width
                && scanBoxAreaRect.top + scanBoxAreaRect.height() <= height) {
            barcode.setCrop(scanBoxAreaRect.left, scanBoxAreaRect.top, scanBoxAreaRect.width(), scanBoxAreaRect.height());
        }

        barcode.setData(data);
        String result = processData(barcode);
        //Log.e("调用zbar 识别","getZbarResult:"+result);
        return  result;
    }

    /**
     * Zxing识别
     */
    private String getZxingResult(byte[] data, int width, int height, boolean isRetry){
        Result rawResult = null;
        Rect scanBoxAreaRect = null;

        try {
            PlanarYUVLuminanceSource source;
            scanBoxAreaRect = mScanBoxView.getScanBoxAreaRect(height);
            if (scanBoxAreaRect != null) {
                source = new PlanarYUVLuminanceSource(data, width, height, scanBoxAreaRect.left, scanBoxAreaRect.top, scanBoxAreaRect.width(),
                        scanBoxAreaRect.height(), false);
            } else {
                source = new PlanarYUVLuminanceSource(data, width, height, 0, 0, width, height, false);
            }

            rawResult = mMultiFormatReader.decodeWithState(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
            if (rawResult == null) {
                rawResult = mMultiFormatReader.decodeWithState(new BinaryBitmap(new HybridBinarizer(source)));
                if (rawResult != null) {
                    BGAQRCodeUtil.d("GlobalHistogramBinarizer 没识别到，HybridBinarizer 能识别到");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mMultiFormatReader.reset();
        }

        if (rawResult == null) {
            return null;
        }

        String result = rawResult.getText();
        //Log.e("调用zxing 识别","getZxingResult:"+result);
        return  result;

    }


    private String processData(Image barcode) {
        if (mScanner.scanImage(barcode) == 0) {
            return null;
        }

        for (Symbol symbol : mScanner.getResults()) {
            // 未能识别的格式继续遍历
            if (symbol.getType() == Symbol.NONE) {
                continue;
            }

            String symData;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
                symData = new String(symbol.getDataBytes(), StandardCharsets.UTF_8);
            } else {
                symData = symbol.getData();
            }
            // 空数据继续遍历
            if (TextUtils.isEmpty(symData)) {
                continue;
            }

            // 处理自动缩放和定位点
            boolean isNeedAutoZoom = isNeedAutoZoom(symbol);
            if (isShowLocationPoint() || isNeedAutoZoom) {
                if (transformToViewCoordinates(symbol.getLocationPoints(), null, isNeedAutoZoom, symData)) {
                    return null;
                } else {
                    return symData;
                }
            } else {
                return symData;
            }
        }
        return null;
    }

    private boolean isNeedAutoZoom(Symbol symbol) {
        return isAutoZoom() && symbol.getType() == Symbol.QRCODE;
    }

    @Override
    protected ScanResult processBitmapData(Bitmap bitmap) {
        try {
            int picWidth = bitmap.getWidth();
            int picHeight = bitmap.getHeight();
            Image barcode = new Image(picWidth, picHeight, "RGB4");
            int[] pix = new int[picWidth * picHeight];
            bitmap.getPixels(pix, 0, picWidth, 0, 0, picWidth, picHeight);
            barcode.setData(pix);
            String result = processData(barcode.convert("Y800"));
            //Log.e("调用zbar 识别","result:"+result);
            if(TextUtils.isEmpty(result)){
                result= ZxingCodeDecoder.syncDecodeQRCode(bitmap);
                //Log.e("调用zxing 识别","result"+result);
            }
            return new ScanResult(result);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}